#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_md5.h>
#include "parseAbst.c"

#define NGX_HTTP_CACHEPROXY_DEFAULT_SHM_SIZE 67108864 // 64 megs
#define MAX_TOKEN_LIMIT  1000000 // for million token user to take same time for playlist 
#define MAX_SHM_URI_ITEM_LIMIT 15 
#define MAX_SHM_URI_LIMIT 5
//#define MAX_PLAYLIST_HASH_IP_LIMIT 3

#define TOKEN_PARA "token"
#define PLAYLIST_VOD_BASEPATH "/data/proxy_vod_dir/vod/"


typedef struct {
  ngx_queue_t  queue;
  void *       data;
  size_t       size;
  u_char       md5[16];
  ngx_uint_t   time;
} item_data_t;

typedef struct {
  ngx_queue_t  queue;
  ngx_queue_t  data;
  ngx_uint_t   len;
  //char       *name;
  ngx_str_t    name;
} uri_item_t;

typedef struct {
  ngx_queue_t  uri_items;
  size_t       len;
  //ngx_atomic_t token; 
  //ngx_int_t   token; 
} ngx_http_cacheproxy_shm_data_t;

typedef struct {
  size_t   shmsize;
  //size_t   tokenbegin;
} ngx_http_cacheproxy_main_conf_t;

typedef struct {
    ngx_str_t                      key_start;
    ngx_str_t                      schema;
    ngx_str_t                      host_header;
    ngx_str_t                      port;
    ngx_str_t                      uri;
} ngx_http_cacheproxy_vars_t;


typedef struct {
    ngx_http_upstream_conf_t       upstream;

    ngx_array_t                   *flushes;
    ngx_array_t                   *body_set_len;
    ngx_array_t                   *body_set;
    ngx_array_t                   *headers_set_len;
    ngx_array_t                   *headers_set;
    ngx_hash_t                     headers_set_hash;

    ngx_array_t                   *headers_source;

    ngx_array_t                   *proxy_lengths;
    ngx_array_t                   *proxy_values;

    ngx_array_t                   *redirects;

    ngx_str_t                      body_source;

    ngx_str_t                      method;
    ngx_str_t                      location;
    ngx_str_t                      url;

#if (NGX_HTTP_CACHE)
    ngx_http_complex_value_t       cache_key;
#endif

    ngx_http_cacheproxy_vars_t          vars;

    ngx_flag_t                     redirect;
    ngx_flag_t                     parse;
    ngx_flag_t                     change_content_length;
    ngx_flag_t                     container;
    ngx_int_t                      tailor;
    ngx_array_t                    *subs;

    ngx_uint_t                     headers_hash_max_size;
    ngx_uint_t                     headers_hash_bucket_size;
    ngx_uint_t                     max_shm_uri_item_limit;
} ngx_http_cacheproxy_loc_conf_t;


typedef struct {
    ngx_http_status_t              status;
    ngx_http_cacheproxy_vars_t          vars;
    size_t                         internal_body_length;
    ngx_buf_t                      *buf;
} ngx_http_cacheproxy_ctx_t;


ngx_shm_zone_t     *ngx_http_cacheproxy_shm_zone = NULL;

//static ngx_uint_t   playlist_matrix[MAX_PLAYLIST_HASH_IP_LIMIT][MAX_SHM_URI_ITEM_LIMIT] = {{0,1,2,3,4,5,6,7,8,9},{9,8,7,6,5,4,3,2,1,0},{5,6,0,7,3,8,2,9,4,1}};

static ngx_str_t    ngx_http_cacheproxy_shm_name = ngx_string("cacheproxy_module");


static ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r,
                                     ngx_http_cacheproxy_ctx_t *ctx, ngx_http_cacheproxy_loc_conf_t *plcf);

static ngx_int_t ngx_http_cacheproxy_create_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_cacheproxy_reinit_request(ngx_http_request_t *r);
static ngx_int_t ngx_http_cacheproxy_process_status_line(ngx_http_request_t *r);
static ngx_int_t ngx_http_cacheproxy_process_header(ngx_http_request_t *r);
static void ngx_http_cacheproxy_abort_request(ngx_http_request_t *r);
static void ngx_http_cacheproxy_finalize_request(ngx_http_request_t *r,
    ngx_int_t rc);



static ngx_int_t ngx_http_cacheproxy_host_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_cacheproxy_port_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t
    ngx_http_cacheproxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t
    ngx_http_cacheproxy_internal_body_length_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data);
static ngx_int_t ngx_http_cacheproxy_add_variables(ngx_conf_t *cf);

static ngx_uint_t
ngx_http_cacheproxy_playlist_index(ngx_http_request_t *r);

static ngx_int_t ngx_event_pipe_copy_cache_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf);

static ngx_int_t
ngx_http_cacheproxy_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data);

#if 0
static ngx_int_t
ngx_http_cacheproxy_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc);
#endif

static ngx_int_t
ngx_http_cacheproxy_copy_content_length(ngx_http_request_t *r, ngx_table_elt_t *h,ngx_uint_t offset);

static ngx_int_t ngx_http_cacheproxy_postconfig(ngx_conf_t *cf);
static void *ngx_http_cacheproxy_create_main_conf(ngx_conf_t *cf);
static char *ngx_http_cacheproxy_init_main_conf(ngx_conf_t *cf,void *conf);
static void *ngx_http_cacheproxy_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_cacheproxy_merge_loc_conf(ngx_conf_t *cf,
    void *parent, void *child);
static ngx_int_t ngx_http_cacheproxy_merge_headers(ngx_conf_t *cf,
    ngx_http_cacheproxy_loc_conf_t *conf, ngx_http_cacheproxy_loc_conf_t *prev);

static char *
ngx_http_cacheproxy_token(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static char *
ngx_http_cacheproxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,void *conf);
char *
ngx_conf_set_mynum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static ngx_int_t   do_output_page(ngx_http_request_t *r,ngx_buf_t *buf);
static ngx_int_t   add_page_to_shm(ngx_http_request_t *r,ngx_buf_t *buf);
static ngx_buf_t*  get_page_from_shm(ngx_http_request_t *r,ngx_uint_t idx);


static u_char * clean_para(ngx_str_t *str,char *key);
static u_char * clean_para2(u_char *str,int _len,char *key);

//static char *ngx_http_cacheproxy_lowat_check(ngx_conf_t *cf, void *post, void *data);

#if (NGX_HTTP_SSL)
static ngx_int_t ngx_http_cacheproxy_set_ssl(ngx_conf_t *cf,
    ngx_http_cacheproxy_loc_conf_t *plcf);
#endif
static void ngx_http_cacheproxy_set_vars(ngx_url_t *u, ngx_http_cacheproxy_vars_t *v);


//static ngx_conf_post_t  ngx_http_proxy_lowat_post =
    //{ ngx_http_cacheproxy_lowat_check };


ngx_module_t  ngx_http_cacheproxy_module;


static ngx_command_t  ngx_http_cacheproxy_commands[] = {

    { ngx_string("cacheproxy_token"),
      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
      ngx_http_cacheproxy_token,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },
    { ngx_string("cacheproxy_pass"),
      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE12,
      ngx_http_cacheproxy_pass,
      NGX_HTTP_LOC_CONF_OFFSET,
      0,
      NULL },
    { ngx_string("cacheproxy_parse"),
      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_cacheproxy_loc_conf_t,parse),
      NULL },
    { ngx_string("cacheproxy_change_cl"),
      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_cacheproxy_loc_conf_t,change_content_length),
      NULL },
    { ngx_string("cacheproxy_is_container"),
      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_cacheproxy_loc_conf_t,container),
      NULL },
    { ngx_string("cacheproxy_tailor"),
      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_mynum_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_cacheproxy_loc_conf_t,tailor),
      NULL },
    { ngx_string("cacheproxy_sub"),
      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE2,
      ngx_conf_set_keyval_slot,
      NGX_HTTP_LOC_CONF_OFFSET,
      offsetof(ngx_http_cacheproxy_loc_conf_t,subs),
      NULL },

      ngx_null_command
};


static ngx_http_module_t  ngx_http_cacheproxy_module_ctx = {
    ngx_http_cacheproxy_add_variables,      /* preconfiguration */
    ngx_http_cacheproxy_postconfig,         /* postconfiguration */

    ngx_http_cacheproxy_create_main_conf,   /* create main configuration */
    ngx_http_cacheproxy_init_main_conf,     /* init main configuration */

    NULL,                                   /* create server configuration */
    NULL,                                   /* merge server configuration */

    ngx_http_cacheproxy_create_loc_conf,    /* create location configration */
    ngx_http_cacheproxy_merge_loc_conf      /* merge location configration */
};


ngx_module_t  ngx_http_cacheproxy_module = {
    NGX_MODULE_V1,
    &ngx_http_cacheproxy_module_ctx,            /* module context */
    ngx_http_cacheproxy_commands,               /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};


static char  ngx_http_cacheproxy_version[] = " HTTP/1.0" CRLF;


static ngx_keyval_t  ngx_http_proxy_headers[] = {
    { ngx_string("Host"), ngx_string("$cacheproxy_host") },
    { ngx_string("Connection"), ngx_string("close") },
    { ngx_string("Keep-Alive"), ngx_string("") },
    { ngx_string("Expect"), ngx_string("") },
    { ngx_null_string, ngx_null_string }
};


static ngx_str_t  ngx_http_proxy_hide_headers[] = {
    ngx_string("Date"),
    ngx_string("Server"),
    ngx_string("X-Pad"),
    ngx_string("X-Accel-Expires"),
    ngx_string("X-Accel-Redirect"),
    ngx_string("X-Accel-Limit-Rate"),
    ngx_string("X-Accel-Buffering"),
    ngx_string("X-Accel-Charset"),
    ngx_null_string
};


#if (NGX_HTTP_CACHE)

static ngx_keyval_t  ngx_http_proxy_cache_headers[] = {
    { ngx_string("Host"), ngx_string("$cacheproxy_host") },
    { ngx_string("Connection"), ngx_string("close") },
    { ngx_string("Keep-Alive"), ngx_string("") },
    { ngx_string("Expect"), ngx_string("") },
    { ngx_string("If-Modified-Since"), ngx_string("") },
    { ngx_string("If-Unmodified-Since"), ngx_string("") },
    { ngx_string("If-None-Match"), ngx_string("") },
    { ngx_string("If-Match"), ngx_string("") },
    { ngx_string("Range"), ngx_string("") },
    { ngx_string("If-Range"), ngx_string("") },
    { ngx_null_string, ngx_null_string }
};

#endif

static ngx_http_variable_t  ngx_http_cacheproxy_vars[] = {

    { ngx_string("cacheproxy_host"), NULL, ngx_http_cacheproxy_host_variable, 0,
      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },

    { ngx_string("cacheproxy_port"), NULL, ngx_http_cacheproxy_port_variable, 0,
      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },

    { ngx_string("cacheproxy_add_x_forwarded_for"), NULL,
      ngx_http_cacheproxy_add_x_forwarded_for_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },

#if 0
    { ngx_string("proxy_add_via"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 },
#endif

    { ngx_string("cacheproxy_internal_body_length"), NULL,
      ngx_http_cacheproxy_internal_body_length_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },

    { ngx_null_string, NULL, NULL, 0, 0, 0 }
};




static ngx_path_init_t  ngx_http_proxy_temp_path = {
    ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 }
};


static ngx_int_t do_output_page(ngx_http_request_t *r,ngx_buf_t *buf)
{
   ngx_buf_t                    *b = buf;
   ngx_chain_t                   out;
   ngx_int_t                     rc;
   off_t                     	 size = (buf->last - buf->pos); 
   ngx_log_t                    *log;

   log = r->connection->log;

   r->root_tested = !r->error_page;
   log->action = "sending data to client";

   r->allow_ranges = 1;
   r->headers_out.status = NGX_HTTP_OK;
   r->headers_out.content_length_n = size;

   if (ngx_http_set_content_type(r) != NGX_OK) {
     return NGX_HTTP_INTERNAL_SERVER_ERROR;
   }


   ngx_log_debug(NGX_LOG_DEBUG_HTTP,log,0,
                      "cache_proxy contentLen:%d",r->headers_out.content_length_n);
   rc = ngx_http_send_header(r);
  
   if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
     return rc;
   }

   /*b = ngx_pcalloc(r->pool,sizeof(ngx_buf_t));
   if(b == NULL){
     return NGX_HTTP_INTERNAL_SERVER_ERROR;
   }*/

   b->memory = 1;
   //b->last_buf = 1;
   b->flush  = 1;
   //b->recycled = 0;
   b->last_in_chain = 1;

   //b->pos  = (u_char*)page->data;
   //b->last = (u_char*)page->data+page->size;

   out.buf = b;
   out.next = NULL;
          
   return ngx_http_output_filter(r,&out);
}


static ngx_buf_t* 
get_page_from_shm(ngx_http_request_t *r,ngx_uint_t idx)
{
    ///ngx_http_cacheproxy_loc_conf_t    *plcf = ngx_http_get_module_loc_conf(r, ngx_http_cacheproxy_module);
    ngx_http_cacheproxy_shm_data_t        *shm_data = (ngx_http_cacheproxy_shm_data_t *) ngx_http_cacheproxy_shm_zone->data;
    ngx_slab_pool_t                       *shpool = (ngx_slab_pool_t *) ngx_http_cacheproxy_shm_zone->shm.addr;
    ngx_log_t                             *log;
    ngx_buf_t                             *buf;
    ngx_uint_t                            i;
    ngx_http_cacheproxy_ctx_t             *ctx;
    //ngx_flag_t                        container = plcf->container;
    
    r->unparsed_uri.data = clean_para(&r->unparsed_uri,TOKEN_PARA);

    log = r->connection->log;

    ctx = ngx_http_get_module_ctx(r, ngx_http_cacheproxy_module);

    ngx_log_debug(NGX_LOG_DEBUG_HTTP, log, 0,
                   "http cacheproxy mem handler  get_page_from_shm pid:%d zone: %p shm_data:%p shpool:%p shm_data.len:%d", 
                   getpid(),ngx_http_cacheproxy_shm_zone,shm_data,shpool,shm_data->len);
    if(ctx && ctx->buf) return ctx->buf;

    uri_item_t  *uri_item = NULL;
    item_data_t *item_data  = NULL; 
    ngx_shmtx_lock(&shpool->mutex);

    if(!ngx_queue_empty(&shm_data->uri_items)){
       ngx_queue_t *next = NULL;
       ngx_queue_t *p = ngx_queue_head(&shm_data->uri_items);
       for(;p != ngx_queue_sentinel(&shm_data->uri_items); p = next){

           uri_item = ngx_queue_data(p,uri_item_t,queue); 

           ngx_log_error(NGX_LOG_ALERT, log, 0,
                         "mem handler get_page_from_shm uri_item name:%V r->unparsed_uri:%V uri_itme->len:%ui , token_idx:%ui",
                          &uri_item->name,&r->unparsed_uri,uri_item->len,idx);

           if(uri_item->name.len == r->unparsed_uri.len && ngx_strncmp(uri_item->name.data,r->unparsed_uri.data,r->unparsed_uri.len) == 0){
           
             if(uri_item->len < (idx+1) ){
               //ngx_shmtx_unlock(&shpool->mutex);
               //return NULL;  
               idx = uri_item->len;
             } 
             ngx_queue_t *item_data_next = NULL;
             ngx_queue_t *data_p = ngx_queue_head(&uri_item->data);

             i = 0;
             for(;data_p != ngx_queue_sentinel(&uri_item->data); data_p = item_data_next){
               if(i < idx){
                  i++;
                  item_data_next = ngx_queue_next(data_p);
                  continue;
               }
               item_data = ngx_queue_data(data_p,item_data_t,queue); 
               buf = ngx_pcalloc(r->pool,sizeof(ngx_buf_t));
               //item_data->size--;
               u_char *d = ngx_pcalloc(r->pool,item_data->size);
               if(d == NULL || buf == NULL){
                 ngx_shmtx_unlock(&shpool->mutex);
                 return NULL;
               }
               ngx_memcpy(d,item_data->data,item_data->size);
               ngx_log_error(NGX_LOG_ALERT, log, 0,
                         "mem handler get_page_from_shm direct to output uri_item name:%V",
                          &uri_item->name);
               buf->pos =  d;
               buf->last = d + item_data->size;
               if(ctx)ctx->buf = buf;
               ngx_shmtx_unlock(&shpool->mutex);
               return buf;
             }
          } 
          next = ngx_queue_next(p);
      } 
    }
    ngx_shmtx_unlock(&shpool->mutex);
    return NULL;
}

#if 0
static ngx_flag_t 
container_is_full(ngx_http_request_t *r)
{
    ngx_http_cacheproxy_loc_conf_t    *plcf = ngx_http_get_module_loc_conf(r, ngx_http_cacheproxy_module);
    ngx_http_cacheproxy_shm_data_t    *shm_data = (ngx_http_cacheproxy_shm_data_t *) ngx_http_cacheproxy_shm_zone->data;
    ngx_slab_pool_t                   *shpool = (ngx_slab_pool_t *) ngx_http_cacheproxy_shm_zone->shm.addr;
    ngx_log_t                         *log;
    ngx_uint_t                        limit = plcf->max_shm_uri_item_limit;
    ngx_flag_t                        container = plcf->container;

    if(container != 1) return 0;

    log = r->connection->log;

    ngx_log_error(NGX_LOG_ALERT, log, 0,
                   "container_is_full %d",plcf->container); 

    uri_item_t *uri_item = NULL;


    ngx_queue_t *next = NULL;
    ngx_queue_t *p = ngx_queue_head(&shm_data->uri_items);

    ngx_shmtx_lock(&shpool->mutex);

    for(;p != ngx_queue_sentinel(&shm_data->uri_items); p = next){
        uri_item = ngx_queue_data(p,uri_item_t,queue); 

        ngx_log_debug(NGX_LOG_DEBUG_HTTP, log, 0,
                         "container_is_full  uri_item name:%s len:%uz",
                          uri_item->name,uri_item->len);

        if(ngx_strncmp(uri_item->name,r->uri.data,r->uri.len) == 0 && 
           uri_item->len == limit){
	     ngx_shmtx_unlock(&shpool->mutex);
             return 1;//is full 
        }
   }

   ngx_shmtx_unlock(&shpool->mutex);
   return 0;
}
#endif

static ngx_int_t 
add_page_to_shm(ngx_http_request_t *r,ngx_buf_t *buf)
{
    ngx_http_cacheproxy_loc_conf_t    *plcf = ngx_http_get_module_loc_conf(r, ngx_http_cacheproxy_module);
    ngx_http_cacheproxy_shm_data_t    *shm_data = (ngx_http_cacheproxy_shm_data_t *) ngx_http_cacheproxy_shm_zone->data;
    ngx_slab_pool_t                   *shpool = (ngx_slab_pool_t *) ngx_http_cacheproxy_shm_zone->shm.addr;
    ngx_log_t                         *log;
    ngx_md5_t                         md5;
    u_char                            md5_buf[16];
    ngx_uint_t                        size = (buf->last - buf->pos);
    ngx_uint_t                        limit = plcf->max_shm_uri_item_limit;
    ngx_flag_t                        container = plcf->container;

    r->unparsed_uri.data = clean_para(&r->unparsed_uri,TOKEN_PARA);
    log = r->connection->log;

    //ngx_log_error(NGX_LOG_ALERT, log, 0,
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, log, 0,
                   "http cacheproxy mem handler add_page_to_shm pid:%d zone: %p shm_data:%p shpool:%p shm_data.len:%d", 
                   getpid(),ngx_http_cacheproxy_shm_zone,shm_data,shpool,shm_data->len);

    uri_item_t *uri_item = NULL;
    item_data_t *item   = NULL; 

    if(size <=0 ) return NGX_DECLINED;

    ngx_md5_init(&md5);
    ngx_md5_update(&md5,buf->pos,size);
    ngx_md5_final(md5_buf, &md5);

    ngx_shmtx_lock(&shpool->mutex);

    ngx_queue_t *next = NULL;
    ngx_queue_t *p = ngx_queue_head(&shm_data->uri_items);

    for(;p != ngx_queue_sentinel(&shm_data->uri_items); p = next){
        uri_item = ngx_queue_data(p,uri_item_t,queue); 

       // ngx_log_error(NGX_LOG_ALERT, log, 0,
        ngx_log_debug(NGX_LOG_DEBUG_HTTP, log, 0,
                         "mem handler add_page_to_shm  uri_item name:%V r->unparsed_uri:%V",
                          &uri_item->name,&r->unparsed_uri);

        if(uri_item->name.len == r->unparsed_uri.len && ngx_strncmp(uri_item->name.data,r->unparsed_uri.data,r->unparsed_uri.len) == 0){
            ngx_queue_t *item_next = NULL;
    	    ngx_queue_t *item_p = ngx_queue_head(&uri_item->data);

            if(container == 1 && uri_item->len == limit){
	        ngx_shmtx_unlock(&shpool->mutex);
                return NGX_BUSY;//is full 
            }

            for(;item_p != ngx_queue_sentinel(&uri_item->data); item_p = item_next){

               item = ngx_queue_data(item_p,item_data_t,queue); 
                            
               if(ngx_strncmp(item->md5,md5_buf,16) == 0){
                  ngx_log_error(NGX_LOG_ALERT, log, 0,
                         "mem handler add_page_to_shm  repeated");
                  ngx_shmtx_unlock(&shpool->mutex);
                  return NGX_AGAIN;//respones repeated 
               }
               item_next = ngx_queue_next(item_p);
            }
          
            ngx_log_error(NGX_LOG_ALERT, log, 0,
                         "mem handler add_page_to_shm  add new item item_datalen:%uz",uri_item->len);
            //add new item_data 
            if((item = ngx_slab_alloc_locked(shpool, sizeof(*item))) == NULL) {
                ngx_log_error(NGX_LOG_EMERG,log,errno,"alloc new item_data error"); 
                ngx_shmtx_unlock(&shpool->mutex);
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }

	    if((item->data = ngx_slab_alloc_locked(shpool,size+1)) == NULL) {
	       ngx_log_error(NGX_LOG_EMERG,log,errno,"alloc segs item->data error"); 
               ngx_shmtx_unlock(&shpool->mutex);
	       return NGX_HTTP_INTERNAL_SERVER_ERROR;
	    }

            //init item_data
            ngx_memzero(item->data,size+1);
            ngx_memcpy(item->data,buf->pos,size);
            item->size = size;
            ngx_memcpy(item->md5,md5_buf,16);
            ngx_queue_insert_head(&uri_item->data,&item->queue);
            uri_item->len++; 
            
            //if(uri_item->len > MAX_SHM_URI_ITEM_LIMIT){
            if(uri_item->len > limit){
               ngx_log_error(NGX_LOG_ALERT, log, 0,
                         "mem handler add_page_to_shm  delete last item  item_datalen:%uz",uri_item->len);
 
               ngx_queue_t *last = ngx_queue_last(&uri_item->data);
               ngx_queue_remove(last);
               uri_item->len--;
               item  = ngx_queue_data(last,item_data_t,queue);
       
               ngx_slab_free_locked(shpool,item->data);
               ngx_slab_free_locked(shpool,item);
            } 
            
            ngx_shmtx_unlock(&shpool->mutex);
            return NGX_OK;
        }

        next = ngx_queue_next(p);
    }


    ngx_log_error(NGX_LOG_ALERT, log, 0,
                         "mem handler add_page_to_shm  add new uri_item  len:%uz",shm_data->len);
    if(shm_data->len >= MAX_SHM_URI_LIMIT){
        ngx_queue_t *last = ngx_queue_last(&shm_data->uri_items);
        ngx_queue_remove(last);
        shm_data->len--;
        uri_item  = ngx_queue_data(last,uri_item_t,queue);

        ngx_queue_t *next = NULL;
        ngx_queue_t *p = ngx_queue_head(&uri_item->data);
        for(;p != ngx_queue_sentinel(&uri_item->data); p = next){
           item = ngx_queue_data(uri_item,item_data_t,queue); 
           ngx_slab_free_locked(shpool,item->data);
           ngx_slab_free_locked(shpool,item);
           next = ngx_queue_next(p);
        }

        ngx_slab_free_locked(shpool,uri_item->name.data);
        ngx_slab_free_locked(shpool,uri_item);
    } 

    //add new uri_item
    if((uri_item = ngx_slab_alloc_locked(shpool, sizeof(*uri_item))) == NULL) {
       ngx_log_error(NGX_LOG_EMERG,log,errno,"alloc new uri_items error"); 
       ngx_shmtx_unlock(&shpool->mutex);
       return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if((item = ngx_slab_alloc_locked(shpool, sizeof(*item))) == NULL) {
       ngx_log_error(NGX_LOG_EMERG,log,errno,"alloc new item_data error"); 
       ngx_shmtx_unlock(&shpool->mutex);
       return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if((item->data = ngx_slab_alloc_locked(shpool,size+1)) == NULL) {
       ngx_log_error(NGX_LOG_EMERG,log,errno,"alloc segs item->data error"); 
       ngx_shmtx_unlock(&shpool->mutex);
       return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    if((uri_item->name.data = ngx_slab_alloc_locked(shpool, (r->unparsed_uri.len+1))) == NULL) {
       ngx_log_error(NGX_LOG_EMERG,log,errno,"alloc segs uri_item->name error"); 
       ngx_shmtx_unlock(&shpool->mutex);
       return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    //add new uri_item
    ngx_queue_init(&uri_item->data);
    uri_item->name.len = r->unparsed_uri.len;
    ngx_memzero(uri_item->name.data,r->unparsed_uri.len+1);
    ngx_memcpy(uri_item->name.data ,r->unparsed_uri.data,r->unparsed_uri.len);
 
    //init item_data
    ngx_memzero(item->data,size+1);
    ngx_memcpy(item->data,buf->pos,size);
    item->size = size;
    ngx_memcpy(item->md5,md5_buf,16);
    uri_item->len = 1;

    ngx_queue_insert_head(&uri_item->data,&item->queue);
    ngx_queue_insert_head(&shm_data->uri_items,&uri_item->queue);

    shm_data->len++;
    ngx_shmtx_unlock(&shpool->mutex);
    return NGX_OK;
}


#if 0
static ngx_int_t
ngx_http_cacheproxy_post_subrequest(ngx_http_request_t *r, void *data, ngx_int_t rc)
{
    
    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "ngx_http_cacheproxy_post_subrequest rc:%uz",rc);
    return NGX_OK;
}
#endif




/* the copy input filter */

static ngx_int_t
ngx_event_pipe_copy_cache_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)
{
    ngx_buf_t    *b;
    ngx_chain_t  *cl;
    ngx_int_t    rc;
    ngx_keyval_t *kv;
    size_t           i;
    //ngx_http_post_subrequest_t     *psr; 
    //ngx_http_request_t             *sr;
    ngx_http_cacheproxy_ctx_t      *ctx;
    ngx_http_cacheproxy_loc_conf_t *plcf;

    ngx_http_request_t *r = (ngx_http_request_t*)p->output_ctx; 

    ctx = ngx_http_get_module_ctx(r, ngx_http_cacheproxy_module);

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_cacheproxy_module);

    if (buf->pos == buf->last) {
        return NGX_OK;
    }

   
    if (p->upstream_eof  || p->upstream_done) {
       ngx_log_debug(NGX_LOG_DEBUG_HTTP,r->connection->log,0,"plcf->tailor :%d",plcf->tailor);
       if(plcf->subs){
          kv = plcf->subs->elts;
	  for (i = 0; i < plcf->subs->nelts; i++) {
            char *k = (char*)kv[i].key.data;
            char *v = (char*)kv[i].value.data;
            size_t len = buf->last-buf->pos;
            u_char *c = buf->pos;
            while( (c=ngx_strnstr(c,k,len)) != NULL){
                *c = *v;
            }
          }
       }
       if(plcf->tailor != 0){
          if(plcf->tailor < 0){
            buf->last += plcf->tailor;
          }else{
            buf->pos += plcf->tailor;
          }
       }
       rc = add_page_to_shm(r,buf);
       if(plcf->parse){
           u_char *parse_buff = ngx_palloc(r->pool,54);
           ngx_memset(parse_buff,' ',54);
           u_char *pb = parse_buff;
           if(rc != NGX_OK && rc!=NGX_AGAIN){
             buf->pos  = parse_buff;
             buf->last = parse_buff+54;
             buf->start = parse_buff;
             buf->end   = parse_buff+54; 
             ngx_sprintf(pb,"error"); 
           }else{
  	      abst_t abst,file_abst;
              FILE   *fp = NULL;
              u_char *playlist_vod_path = ngx_pcalloc(r->pool,sizeof(PLAYLIST_VOD_BASEPATH)+r->uri.len);
              u_char *p = playlist_vod_path;
              if(!playlist_vod_path){
                return NGX_ERROR;
              }
              p = ngx_cpystrn(p,(u_char*)PLAYLIST_VOD_BASEPATH,sizeof(PLAYLIST_VOD_BASEPATH)-1);
              ngx_cpystrn(p,r->uri.data,r->uri.len+1);
              ngx_log_error(NGX_LOG_ALERT,r->connection->log,0,"vod path:%s %d",playlist_vod_path,(sizeof(PLAYLIST_VOD_BASEPATH)+r->uri.len+1));

              abst.buff_b = (char*)buf->pos;
	      abst.len    = buf->last - buf->pos;
              parseAbst((char*)buf->pos,&abst);

              int ret = parseAbstByPath((char*)playlist_vod_path,&file_abst,&fp);
              ngx_log_error(NGX_LOG_ALERT,r->connection->log,0,"parseAbstByPath ret:%d",ret);
              if(ret == -2){ //not exists go create file
                 ret = create_abst_file((char*)playlist_vod_path,abst.buff_b,abst.len);
                 ngx_log_error(NGX_LOG_ALERT,r->connection->log,0,"create_abst_file ret:%d",ret);
              }else if(ret <= -10){ //error
                //return NGX_ERROR;
                ngx_log_error(NGX_LOG_ALERT,r->connection->log,0,"parseAbstByPath rise error ret:%d",ret);
              }else if(file_abst.lastfragidx < abst.lastfragidx){ //has new frag
                if(abst.fragmentcount > 0 && abst.fragmentruntables){
                     u_int8_t i;
                     for(i=0;i<abst.fragmentcount;i++){
                       fragmentruntable_t *frag = &abst.fragmentruntables[i];
                       if(frag->fragmentrunentrycount > 0 && frag->fragmentrunentry){
                          u_int8_t j;
                          u_int8_t added = 0;
                          for(j=0;j<frag->fragmentrunentrycount;j++){
                             fragmentrunentry_t *fragentry = &frag->fragmentrunentry[j];
                             if(fragentry->firstfrag > file_abst.lastfragidx){
			        append_fragmentrunentry(fp,fragentry);
                                added++;
                             }
                          }
                          if(added){
                             u_int32_t cnt = file_abst.fragmentruntables[0].fragmentrunentrycount + added;
                             ngx_log_error(NGX_LOG_ALERT,r->connection->log,0,"fuck bug : file_abst.fragmentruntables[0].fragmentrunentrycount:%d added:%d"
                                                "fragmentrunentrycount_pos:%d cnt:%d",
                                              file_abst.fragmentruntables[0].fragmentrunentrycount,added,file_abst.fragmentrunentrycount_pos,cnt);
                             if(cnt <= 0){
                               break;
                             }
			     repeat_fragmentrunentrycount(fp,file_abst.fragmentrunentrycount_pos,cnt);
                          }
                       }
                     }
                }
                fclose(fp);
                destoryAbst(&file_abst);
              }

              buf->pos  = parse_buff;
              buf->last = parse_buff+54;
              buf->start = parse_buff;
              buf->end   = parse_buff+54; 
             
              if(abst.fragmentcount > 0 && abst.fragmentruntables){
                 u_int8_t i;
                 for(i=0;i<abst.fragmentcount;i++){
                   fragmentruntable_t *rt = &abst.fragmentruntables[i];
                   if(rt->fragmentrunentrycount > 0 && rt->fragmentrunentry){
                      u_int8_t j;
                      for(j=0;j<rt->fragmentrunentrycount;j++){
                         fragmentrunentry_t *re = &rt->fragmentrunentry[j];
                         ngx_log_debug(NGX_LOG_DEBUG_HTTP,r->connection->log,0,"seg:%u\n",re->firstfrag);
   		         pb = ngx_sprintf(pb,"%uz,",re->firstfrag);
   		         //pb = ngx_sprintf(pb,"%c",",");
     
                         /*ngx_str_set(&location,"/cuclive/livestream/manifest.f4m");
                         psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
                         if (psr == NULL) {
                             return NGX_HTTP_INTERNAL_SERVER_ERROR;
                         }
                         psr->handler = ngx_http_cacheproxy_post_subrequest;
                         psr->data = NULL;
                     
                         rc = ngx_http_subrequest(r, &location, NULL, &sr, psr, 0);
                      
                         if(rc != NGX_OK){
                            return NGX_ERROR;
                         }*/
     
                      }
                   }
                 } 
              }
              destoryAbst(&abst);
           }
       //}else if(rc == NGX_OK){ //has added new one playlist
       }else if(ctx->buf){ //playlist has old version data;
           ngx_buf_t *page = ctx->buf;
           //if((page=get_page_from_shm(r,2))){
           ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "mem handler get_page_from_shm:get-buf-from-shm prevdata:%s prevsize:%uz orign_data:%s orign_len:%uz",
                            page->pos,(page->last - page->pos),buf->pos,(buf->last - buf->pos));
           buf->pos  = page->pos;
           buf->last = page->last;
           buf->start = page->start;
           buf->end   = page->end; 
           ctx->buf = NULL; 
      }
    }

    if (p->free) {
        cl = p->free;
        b = cl->buf;
        p->free = cl->next;
        ngx_free_chain(p->pool, cl);

    } else {
        b = ngx_alloc_buf(p->pool);
        if (b == NULL) {
            return NGX_ERROR;
        }
    }

    ngx_memcpy(b, buf, sizeof(ngx_buf_t));
    b->shadow = buf;
    b->tag = p->tag;
    b->last_shadow = 1;
    b->recycled = 1;
    buf->shadow = b;

    cl = ngx_alloc_chain_link(p->pool);
    if (cl == NULL) {
        return NGX_ERROR;
    }

    cl->buf = b;
    cl->next = NULL;

    ngx_log_debug(NGX_LOG_DEBUG_HTTP, p->log, 0, "input buf #%d addr_text:%V", b->num,&r->connection->addr_text);
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, p->log, 0, "data:%s size:%d",b->pos,(b->last - b->pos));

    if (p->in) {
        *p->last_in = cl;
    } else {
        p->in = cl;
    }
    p->last_in = &cl->next;
    return NGX_OK;
}


static ngx_int_t
ngx_http_cacheproxy_token_handler(ngx_http_request_t *r)
{
    //ngx_http_cacheproxy_main_conf_t *cpmf = ngx_http_get_module_main_conf(r,ngx_http_cacheproxy_module);
    //ngx_http_cacheproxy_shm_data_t  *shm_data = (ngx_http_cacheproxy_shm_data_t *) ngx_http_cacheproxy_shm_zone->data;
    //ngx_slab_pool_t               *shpool = (ngx_slab_pool_t *) ngx_http_cacheproxy_shm_zone->shm.addr;
    ngx_log_t                       *log;
    ngx_buf_t                       *buf;

    log = r->connection->log;


    /*ngx_shmtx_lock(&shpool->mutex);

    if(shm_data->token == -1){
       shm_data->token = cpmf->tokenbegin;
    }

    ngx_int_t token = shm_data->token++;

    ngx_log_debug(NGX_LOG_DEBUG_HTTP, log, 0,"ngx_http_cacheproxy_token_handler begin pid:%d token:%A",getpid(),token);

    //ngx_atomic_cmp_set(&shm_data->token, (ngx_atomic_uint_t)MAX_TOKEN_LIMIT,(ngx_atomic_uint_t)cpmf->tokenbegin); 
    if(shm_data->token >= MAX_TOKEN_LIMIT )
       shm_data->token = cpmf->tokenbegin;

    ngx_shmtx_unlock(&shpool->mutex);
    */

    u_char    *token_buff;
    size_t    len = create_token(&token_buff);
    if(len <= 0){
     return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    u_char *b = ngx_pcalloc(r->pool,len+1);
    buf = ngx_pcalloc(r->pool,sizeof(ngx_buf_t));
    if(buf == NULL || b == NULL){
     return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    //size_t len  = ngx_sprintf(b,"%A",token) - b;
    ngx_cpystrn(b,token_buff,len+1);
    free(token_buff);

    buf->pos   = b;
    buf->last  = b+len;
    buf->start = b;
    buf->end   = b+len;

    ngx_log_debug(NGX_LOG_DEBUG_HTTP, log, 0,"ngx_http_cacheproxy_token_handler begin pid:%d token:%s",getpid(),b);
    return do_output_page(r,buf);
}


static ngx_int_t
ngx_http_cacheproxy_handler(ngx_http_request_t *r)
{
    ngx_http_cacheproxy_loc_conf_t    *plcf = ngx_http_get_module_loc_conf(r, ngx_http_cacheproxy_module);
    ngx_int_t                       rc;
    ngx_http_upstream_t             *u;
    ngx_http_cacheproxy_ctx_t       *ctx;
    //ngx_http_cacheproxy_loc_conf_t  *plcf;
    ngx_buf_t                       *page;
    ngx_log_t                       *log = r->connection->log;
    ngx_flag_t                        container = plcf->container;

    if( container == 1 && (page=get_page_from_shm(r,ngx_http_cacheproxy_playlist_index(r))) ){
       ngx_log_debug(NGX_LOG_DEBUG_HTTP, log, 0,"ngx_http_cacheproxy_handler uri:%V get container full data",&r->uri);
       return do_output_page(r,page);
    }

    if (ngx_http_upstream_create(r) != NGX_OK) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_cacheproxy_ctx_t));
    if (ctx == NULL) {
        return NGX_ERROR;
    }

    ngx_http_set_ctx(r, ctx, ngx_http_cacheproxy_module);

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_cacheproxy_module);

    u = r->upstream;

    if (plcf->proxy_lengths == NULL) {
        ctx->vars = plcf->vars;
        u->schema = plcf->vars.schema;
#if (NGX_HTTP_SSL)
        u->ssl = (plcf->upstream.ssl != NULL);
#endif

    } else {
        if (ngx_http_proxy_eval(r, ctx, plcf) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    }

    u->output.tag = (ngx_buf_tag_t) &ngx_http_cacheproxy_module;

    u->conf = &plcf->upstream;

#if (NGX_HTTP_CACHE)
    //u->create_key = ngx_http_proxy_create_key;
#endif
    u->create_request = ngx_http_cacheproxy_create_request;
    u->reinit_request = ngx_http_cacheproxy_reinit_request;
    u->process_header = ngx_http_cacheproxy_process_status_line;
    u->abort_request = ngx_http_cacheproxy_abort_request;
    u->finalize_request = ngx_http_cacheproxy_finalize_request;
    r->state = 0;

    /*if (plcf->redirects) {
        u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
    }*/

    u->buffering = plcf->upstream.buffering;

    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));
    if (u->pipe == NULL) {
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }

    u->pipe->input_filter = ngx_event_pipe_copy_cache_input_filter;
    u->pipe->read_max_length = 0;

    u->accel = 1;

    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    return NGX_DONE;
}


static ngx_int_t
ngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_cacheproxy_ctx_t *ctx,
    ngx_http_cacheproxy_loc_conf_t *plcf)
{
    u_char               *p;
    size_t                add;
    u_short               port;
    ngx_str_t             proxy;
    ngx_url_t             url;
    ngx_http_upstream_t  *u;

    if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0,
                            plcf->proxy_values->elts)
        == NULL)
    {
        return NGX_ERROR;
    }

    if (proxy.len > 7
        && ngx_strncasecmp(proxy.data, (u_char *) "http://", 7) == 0)
    {
        add = 7;
        port = 80;

#if (NGX_HTTP_SSL)

    } else if (proxy.len > 8
               && ngx_strncasecmp(proxy.data, (u_char *) "https://", 8) == 0)
    {
        add = 8;
        port = 443;
        r->upstream->ssl = 1;

#endif

    } else {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "invalid URL prefix in \"%V\"", &proxy);
        return NGX_ERROR;
    }

    u = r->upstream;

    u->schema.len = add;
    u->schema.data = proxy.data;

    ngx_memzero(&url, sizeof(ngx_url_t));

    url.url.len = proxy.len - add;
    url.url.data = proxy.data + add;
    url.default_port = port;
    url.uri_part = 1;
    url.no_resolve = 1;

    if (ngx_parse_url(r->pool, &url) != NGX_OK) {
        if (url.err) {
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "%s in upstream \"%V\"", url.err, &url.url);
        }

        return NGX_ERROR;
    }

    if (url.uri.len) {
        if (url.uri.data[0] == '?') {
            p = ngx_pnalloc(r->pool, url.uri.len + 1);
            if (p == NULL) {
                return NGX_ERROR;
            }

            *p++ = '/';
            ngx_memcpy(p, url.uri.data, url.uri.len);

            url.uri.len++;
            url.uri.data = p - 1;
        }

    } else {
        url.uri = r->unparsed_uri;
    }

    ctx->vars.key_start = u->schema;

    ngx_http_cacheproxy_set_vars(&url, &ctx->vars);

    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));
    if (u->resolved == NULL) {
        return NGX_ERROR;
    }

    if (url.addrs && url.addrs[0].sockaddr) {
        u->resolved->sockaddr = url.addrs[0].sockaddr;
        u->resolved->socklen = url.addrs[0].socklen;
        u->resolved->naddrs = 1;
        u->resolved->host = url.addrs[0].name;

    } else {
        u->resolved->host = url.host;
        u->resolved->port = (in_port_t) (url.no_port ? port : url.port);
        u->resolved->no_port = url.no_port;
    }

    return NGX_OK;
}


static u_char * clean_para2(u_char *str,int _len,char *key)
{
    if(!key) return str;
    size_t klen = ngx_strlen(key);
    size_t slen = _len == -1 ? ngx_strlen(str) : (size_t)_len;

    u_char *b = ngx_strnstr(str,key,slen);
    u_char *e = str + slen;
    if(!b) return str;
    u_char *b2 = ngx_strnstr(b,"&",slen - klen);
    if(b2){
     klen = b2 - b + 1;
     if(b==str){//at first
        return ++b2;
     }else{
       size_t i  = 0;
       for(;i<klen;i++){
         u_char *c = b;
         u_char *n = c + 1;
         while(n<=e){
            //u_char s = *c;
            *c = *n;
            *n = ' ';
            n++;
            c++;
         }
       }
       *(str+(slen - klen + 1))='\0';
     }
     return str;
    }else{
     if(b==str){
       *b='\0';
     }else{
       *(--b)='\0';
     }
     return str;
    }
}

static u_char * clean_para(ngx_str_t *str,char *key)
{
    if(!key) return str->data;
    size_t len = ngx_strlen(key);
    u_char *b = ngx_strnstr(str->data,key,str->len);
    u_char *e = str->data + str->len;
    if(!b) return str->data;
    u_char *b2 = ngx_strnstr(b,"&",str->len - len);
    if(b2){
     len = b2 - b + 1;
     if(b==str->data){//at first
        str->data = ++b2;
        str->len  -= len;
     }else{
       size_t i  = 0;
       for(;i<len;i++){
         u_char *c = b;
         u_char *n = c + 1;
         while(n<=e){
            u_char s = *c;
            *c = *n;
            *n = s; 
            //if(n==e)break;
            n++;
            c++;
         } 
       }
       str->len -= len;
     }
     return str->data;
    }else{
     str->len = b - str->data - 1;
     return str->data;
    }
}

static ngx_int_t
ngx_http_cacheproxy_create_request(ngx_http_request_t *r)
{
    size_t                        len, uri_len, loc_len, body_len;
    uintptr_t                     escape;
    ngx_buf_t                    *b;
    ngx_str_t                     method;
    ngx_uint_t                    i, unparsed_uri;
    ngx_chain_t                  *cl, *body;
    ngx_list_part_t              *part;
    ngx_table_elt_t              *header;
    ngx_http_upstream_t          *u;
    ngx_http_cacheproxy_ctx_t         *ctx;
    ngx_http_script_code_pt       code;
    ngx_http_script_engine_t      e, le;
    ngx_http_cacheproxy_loc_conf_t    *plcf;
    ngx_http_script_len_code_pt   lcode;

    u = r->upstream;

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_cacheproxy_module);
 
    /*if ( r->args.len > 0 ) {
        r->args.data = clean_para(&r->args,"token");
    }*/
    //ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                  //"args:%V",&r->args);

    if (u->method.len) {
        /* HEAD was changed to GET to cache response */
        method = u->method;
        method.len++;

    } else if (plcf->method.len) {
        method = plcf->method;

    } else {
        method = r->method_name;
        method.len++;
    }

    len = method.len + sizeof(ngx_http_cacheproxy_version) - 1 + sizeof(CRLF) - 1;

    escape = 0;
    loc_len = 0;
    unparsed_uri = 0;

    ctx = ngx_http_get_module_ctx(r, ngx_http_cacheproxy_module);

    if (plcf->proxy_lengths) {
        uri_len = ctx->vars.uri.len;

    } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri && r == r->main)
    {
        unparsed_uri = 1;
        ///r->unparsed_uri.data = clean_para(&r->unparsed_uri,"token");
        uri_len = r->unparsed_uri.len;

    } else {
        loc_len = (r->valid_location && ctx->vars.uri.len) ?
                      plcf->location.len : 0;

        if (r->quoted_uri || r->space_in_uri || r->internal) {
            escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,
                                        r->uri.len - loc_len, NGX_ESCAPE_URI);
        }

        uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape
                  + sizeof("?") - 1 + r->args.len;
    }

    if (uri_len == 0) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "zero length URI to proxy");
        return NGX_ERROR;
    }

    len += uri_len;

    ngx_http_script_flush_no_cacheable_variables(r, plcf->flushes);

    if (plcf->body_set_len) {
        le.ip = plcf->body_set_len->elts;
        le.request = r;
        le.flushed = 1;
        body_len = 0;

        while (*(uintptr_t *) le.ip) {
            lcode = *(ngx_http_script_len_code_pt *) le.ip;
            body_len += lcode(&le);
        }

        ctx->internal_body_length = body_len;
        len += body_len;
    }

    le.ip = plcf->headers_set_len->elts;
    le.request = r;
    le.flushed = 1;

    while (*(uintptr_t *) le.ip) {
        while (*(uintptr_t *) le.ip) {
            lcode = *(ngx_http_script_len_code_pt *) le.ip;
            len += lcode(&le);
        }
        le.ip += sizeof(uintptr_t);
    }


    if (plcf->upstream.pass_request_headers) {
        part = &r->headers_in.headers.part;
        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {
                if (part->next == NULL) {
                    break;
                }

                part = part->next;
                header = part->elts;
                i = 0;
            }

            if (ngx_hash_find(&plcf->headers_set_hash, header[i].hash,
                              header[i].lowcase_key, header[i].key.len))
            {
                continue;
            }

            len += header[i].key.len + sizeof(": ") - 1
                + header[i].value.len + sizeof(CRLF) - 1;
        }
    }


    b = ngx_create_temp_buf(r->pool, len);
    if (b == NULL) {
        return NGX_ERROR;
    }

    ngx_memzero(b->pos, len);

    cl = ngx_alloc_chain_link(r->pool);
    if (cl == NULL) {
        return NGX_ERROR;
    }

    cl->buf = b;


    /* the request line */

    b->last = ngx_copy(b->last, method.data, method.len);

    u->uri.data = b->last;

    if (plcf->proxy_lengths) {
        b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
    } else if (unparsed_uri) {
        b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len);
    } else {
        if (r->valid_location) {
            b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);
        }

        if (escape) {
            ngx_escape_uri(b->last, r->uri.data + loc_len,
                           r->uri.len - loc_len, NGX_ESCAPE_URI);
            b->last += r->uri.len - loc_len + escape;

        } else {
            b->last = ngx_copy(b->last, r->uri.data + loc_len,
                               r->uri.len - loc_len);
        }

        if (r->args.len > 0) {
            *b->last++ = '?';
            b->last = ngx_copy(b->last,r->args.data,r->args.len);
        }
    }
    size_t l     = b->last - u->uri.data;
    u->uri.data  = clean_para2(u->uri.data,l,TOKEN_PARA);
    u->uri.len   = ngx_strlen(u->uri.data);
    b->last      = u->uri.data + u->uri.len;

    b->last = ngx_cpymem(b->last, ngx_http_cacheproxy_version,
                         sizeof(ngx_http_cacheproxy_version) - 1);

    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));

    e.ip = plcf->headers_set->elts;
    e.pos = b->last;
    e.request = r;
    e.flushed = 1;

    le.ip = plcf->headers_set_len->elts;

    while (*(uintptr_t *) le.ip) {
        lcode = *(ngx_http_script_len_code_pt *) le.ip;

        /* skip the header line name length */
        (void) lcode(&le);

        if (*(ngx_http_script_len_code_pt *) le.ip) {

            for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {
                lcode = *(ngx_http_script_len_code_pt *) le.ip;
            }

            e.skip = (len == sizeof(CRLF) - 1) ? 1 : 0;

        } else {
            e.skip = 0;
        }

        le.ip += sizeof(uintptr_t);

        while (*(uintptr_t *) e.ip) {
            code = *(ngx_http_script_code_pt *) e.ip;
            code((ngx_http_script_engine_t *) &e);
        }
        e.ip += sizeof(uintptr_t);
    }

    b->last = e.pos;


    if (plcf->upstream.pass_request_headers) {
        part = &r->headers_in.headers.part;
        header = part->elts;

        for (i = 0; /* void */; i++) {

            if (i >= part->nelts) {
                if (part->next == NULL) {
                    break;
                }

                part = part->next;
                header = part->elts;
                i = 0;
            }

            if (ngx_hash_find(&plcf->headers_set_hash, header[i].hash,
                              header[i].lowcase_key, header[i].key.len))
            {
                continue;
            }

            b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);

            *b->last++ = ':'; *b->last++ = ' ';

            b->last = ngx_copy(b->last, header[i].value.data,
                               header[i].value.len);

            *b->last++ = CR; *b->last++ = LF;

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http proxy header: \"%V: %V\"",
                           &header[i].key, &header[i].value);
        }
    }


    /* add "\r\n" at the header end */
    *b->last++ = CR; *b->last++ = LF;

    if (plcf->body_set) {
        e.ip = plcf->body_set->elts;
        e.pos = b->last;

        while (*(uintptr_t *) e.ip) {
            code = *(ngx_http_script_code_pt *) e.ip;
            code((ngx_http_script_engine_t *) &e);
        }

        b->last = e.pos;
    }

    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http proxy header:\n\"%*s\" add_text:%V",
                   (size_t) (b->last - b->pos), b->pos,&r->connection->addr_text);

    if (plcf->body_set == NULL && plcf->upstream.pass_request_body) {

        body = u->request_bufs;
        u->request_bufs = cl;

        while (body) {
            b = ngx_alloc_buf(r->pool);
            if (b == NULL) {
                return NGX_ERROR;
            }

            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));

            cl->next = ngx_alloc_chain_link(r->pool);
            if (cl->next == NULL) {
                return NGX_ERROR;
            }

            cl = cl->next;
            cl->buf = b;

            body = body->next;
        }

    } else {
        u->request_bufs = cl;
    }

    b->flush = 1;
    cl->next = NULL;

    return NGX_OK;
}


static ngx_int_t
ngx_http_cacheproxy_reinit_request(ngx_http_request_t *r)
{
    ngx_http_cacheproxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_cacheproxy_module);

    if (ctx == NULL) {
        return NGX_OK;
    }

    ctx->status.code = 0;
    ctx->status.count = 0;
    ctx->status.start = NULL;
    ctx->status.end = NULL;

    r->upstream->process_header = ngx_http_cacheproxy_process_status_line;
    r->state = 0;

    return NGX_OK;
}


static ngx_int_t
ngx_http_cacheproxy_process_status_line(ngx_http_request_t *r)
{
    size_t                 len;
    ngx_int_t              rc;
    ngx_http_upstream_t   *u;
    ngx_http_cacheproxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_cacheproxy_module);

    if (ctx == NULL) {
        return NGX_ERROR;
    }

    u = r->upstream;

    rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status);

    if (rc == NGX_AGAIN) {
        return rc;
    }

    if (rc == NGX_ERROR) {

#if (NGX_HTTP_CACHE)

        if (r->cache) {
            r->http_version = NGX_HTTP_VERSION_9;
            return NGX_OK;
        }

#endif

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "upstream sent no valid HTTP/1.0 header");

#if 0
        if (u->accel) {
            return NGX_HTTP_UPSTREAM_INVALID_HEADER;
        }
#endif

        r->http_version = NGX_HTTP_VERSION_9;
        u->state->status = NGX_HTTP_OK;

        return NGX_OK;
    }

    if (u->state) {
        u->state->status = ctx->status.code;
    }

    u->headers_in.status_n = ctx->status.code;

    len = ctx->status.end - ctx->status.start;
    u->headers_in.status_line.len = len;

    u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);
    if (u->headers_in.status_line.data == NULL) {
        return NGX_ERROR;
    }

    ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len);

    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "http proxy status %ui \"%V\"",
                   u->headers_in.status_n, &u->headers_in.status_line);

    u->process_header = ngx_http_cacheproxy_process_header;

    return ngx_http_cacheproxy_process_header(r);
}


static ngx_int_t
ngx_http_cacheproxy_process_header(ngx_http_request_t *r)
{
    ngx_int_t                       rc;
    ngx_table_elt_t                *h;
    ngx_http_upstream_header_t     *hh;
    ngx_http_upstream_main_conf_t  *umcf;

    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);

    for ( ;; ) {

        rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);

        if (rc == NGX_OK) {

            /* a header line has been parsed successfully */

            h = ngx_list_push(&r->upstream->headers_in.headers);
            if (h == NULL) {
                return NGX_ERROR;
            }

            h->hash = r->header_hash;

            h->key.len = r->header_name_end - r->header_name_start;

            h->value.len = r->header_end - r->header_start;
            h->key.data = ngx_pnalloc(r->pool,
                               h->key.len + 1 + h->value.len + 1 + h->key.len);
            if (h->key.data == NULL) {
                   return NGX_ERROR;
            }
   
            h->value.data = h->key.data + h->key.len + 1;
            h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;
   
            ngx_cpystrn(h->key.data, r->header_name_start, h->key.len + 1);
            ngx_cpystrn(h->value.data, r->header_start, h->value.len + 1);

            if (h->key.len == r->lowcase_index) {
                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
            } else {
                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
            }

            hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,
                               h->lowcase_key, h->key.len);
         
            if(hh && ngx_strncmp(h->key.data,"Content-Length",h->key.len)==0){
               hh->copy_handler  = ngx_http_cacheproxy_copy_content_length;
            }
            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {
                return NGX_ERROR;
            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http proxy header: \"%V: %V\"",
                           &h->key, &h->value);

            continue;
        }

        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {

            /* a whole header has been parsed successfully */

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http proxy header done");

            /*
             * if no "Server" and "Date" in header line,
             * then add the special empty headers
             */

            if (r->upstream->headers_in.server == NULL) {
                h = ngx_list_push(&r->upstream->headers_in.headers);
                if (h == NULL) {
                    return NGX_ERROR;
                }

                h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(
                                    ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r');

                ngx_str_set(&h->key, "Server");
                ngx_str_null(&h->value);
                h->lowcase_key = (u_char *) "server";
            }

            if (r->upstream->headers_in.date == NULL) {
                h = ngx_list_push(&r->upstream->headers_in.headers);
                if (h == NULL) {
                    return NGX_ERROR;
                }

                h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e');

                ngx_str_set(&h->key, "Date");
                ngx_str_null(&h->value);
                h->lowcase_key = (u_char *) "date";
            }

            return NGX_OK;
        }

        if (rc == NGX_AGAIN) {
            return NGX_AGAIN;
        }

        /* there was error while a header line parsing */

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "upstream sent invalid header");

        return NGX_HTTP_UPSTREAM_INVALID_HEADER;
    }
}

static ngx_uint_t
ngx_http_cacheproxy_playlist_index(ngx_http_request_t *r)
{
  
   ngx_http_cacheproxy_loc_conf_t    *plcf = ngx_http_get_module_loc_conf(r, ngx_http_cacheproxy_module);
   ngx_uint_t                        limit = plcf->max_shm_uri_item_limit;

   in_addr_t  ip = ((struct sockaddr_in*)(r->connection->sockaddr))->sin_addr.s_addr;
   size_t     len = sizeof(TOKEN_PARA"=") - 1;
   if(r->args.len == 0){
     return ip%limit;  // only ip hash
   }
 
   u_char   *b = ngx_strnstr(r->args.data,TOKEN_PARA"=",r->args.len);
   if(!b){
     return ip%limit;  // only ip hash
   }
   b = b + len;
   u_char   *e = ngx_strnstr(b,"&",r->args.len -len);
   if(!e){
     e = r->args.data + r->args.len;
   }   

   if(e <= b){
     return ip%limit;  // only ip hash
   }
  
   //ngx_int_t token = ngx_atoi(b,e-b);
   u_int64_t token =   parse_token(b,e-b);
   if(token == 0){
     return ip%limit;  // only ip hash
   }

   //ngx_uint_t ip_idx = ip%MAX_PLAYLIST_HASH_IP_LIMIT; 
   ngx_uint_t token_idx = (token%limit) & (~1);
   ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                 "playlist_index  token:%L token_idx:%ui",token,token_idx);
   //return playlist_matrix[ip_idx][token_idx];
   return token_idx; //only token hash
}
 
static ngx_int_t
ngx_http_cacheproxy_copy_content_length(ngx_http_request_t *r, ngx_table_elt_t *h,
    ngx_uint_t offset)
{
    ngx_table_elt_t  *ho;
    ngx_buf_t *page = NULL;
    ngx_str_t val;
    ngx_http_cacheproxy_loc_conf_t *plcf;
   
    ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
              "ngx_http_cacheproxy_copy_content_length orign:%v addr_text:%V",&h->value,&r->connection->addr_text);

    plcf = ngx_http_get_module_loc_conf(r, ngx_http_cacheproxy_module);

    r->upstream->pipe->read_max_length = ngx_atoof(h->value.data, h->value.len);

    if(plcf->parse){
       ngx_str_set(&val,"54");//for robot
       h->value = val; 
    }else if(plcf->change_content_length && (ngx_strncmp(h->value.data,(u_char*)"0",1) !=0 ) && (page=get_page_from_shm(r,ngx_http_cacheproxy_playlist_index(r)))){
       u_char len_buff[10] = {'0'};
       int  len  = ngx_sprintf(len_buff, "%uz",(page->last - page->pos)) - len_buff;
       val.data = ngx_pnalloc(r->pool,len+1);
       val.len = len;
       ngx_cpystrn(val.data,(u_char*)len_buff, len + 1);
       h->value = val; 
       //ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
       ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
              "ngx_http_cacheproxy_copy_content_length change Content-Length:%v read_max_length:%uz",&h->value,r->upstream->pipe->read_max_length );
    }else if(plcf->tailor != 0){
       //if(plcf->tailor < 0){
       //}else{
       //}
       ngx_http_clear_content_length(r);
       ngx_http_clear_accept_ranges(r);
       return NGX_OK;
    }

    ho = ngx_list_push(&r->headers_out.headers);
    if (ho == NULL) {
        return NGX_ERROR;
    }

    *ho = *h;

    r->headers_out.content_length = ho;
    r->headers_out.content_length_n = ngx_atoof(h->value.data, h->value.len);

    return NGX_OK;
}


static void
ngx_http_cacheproxy_abort_request(ngx_http_request_t *r)
{
    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "abort http proxy request");

    return;
}


static void
ngx_http_cacheproxy_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                   "finalize http proxy request");

    return;
}


static void *
ngx_http_cacheproxy_create_main_conf(ngx_conf_t *cf)
{
    ngx_http_cacheproxy_main_conf_t  *conf;

    conf = ngx_palloc(cf->pool, sizeof(ngx_http_cacheproxy_main_conf_t));
    if (conf == NULL) {
        return NULL;
    }
    conf->shmsize = NGX_CONF_UNSET_SIZE;
    //conf->tokenbegin = 0;//NGX_CONF_UNSET_SIZE;
    return conf;
}


static char *
ngx_http_cacheproxy_init_main_conf(ngx_conf_t *cf, void *conf)
{
    ngx_http_cacheproxy_main_conf_t  *cpmf = conf;

    ngx_conf_init_size_value(cpmf->shmsize, NGX_HTTP_CACHEPROXY_DEFAULT_SHM_SIZE);
    //ngx_conf_init_size_value(cpmf->tokenbegin,0);  //inited by loc_conf
    //ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "init_main cpmf->tokenbegin %z",cpmf->tokenbegin);
    return NGX_CONF_OK;
}



static void *
ngx_http_cacheproxy_create_loc_conf(ngx_conf_t *cf)
{
    ngx_http_cacheproxy_loc_conf_t  *conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_cacheproxy_loc_conf_t));
    if (conf == NULL) {
        return NULL;
    }

    /*
     * set by ngx_pcalloc():
     *
     *     conf->upstream.bufs.num = 0;
     *     conf->upstream.ignore_headers = 0;
     *     conf->upstream.next_upstream = 0;
     *     conf->upstream.cache_use_stale = 0;
     *     conf->upstream.cache_methods = 0;
     *     conf->upstream.temp_path = NULL;
     *     conf->upstream.hide_headers_hash = { NULL, 0 };
     *     conf->upstream.uri = { 0, NULL };
     *     conf->upstream.location = NULL;
     *     conf->upstream.store_lengths = NULL;
     *     conf->upstream.store_values = NULL;
     *
     *     conf->method = NULL;
     *     conf->headers_source = NULL;
     *     conf->headers_set_len = NULL;
     *     conf->headers_set = NULL;
     *     conf->headers_set_hash = NULL;
     *     conf->body_set_len = NULL;
     *     conf->body_set = NULL;
     *     conf->body_source = { 0, NULL };
     *     conf->redirects = NULL;
     */

    conf->upstream.store = NGX_CONF_UNSET;
    conf->upstream.store_access = NGX_CONF_UNSET_UINT;
    conf->upstream.buffering = NGX_CONF_UNSET;
    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;

    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;
    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;
    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;

    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;
    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;

    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;
    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;
    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;

    conf->upstream.pass_request_headers = NGX_CONF_UNSET;
    conf->upstream.pass_request_body = NGX_CONF_UNSET;

#if (NGX_HTTP_CACHE)
    conf->upstream.cache = NGX_CONF_UNSET_PTR;
    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;
    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;
    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;
    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;
#endif

    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;
    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;

    conf->upstream.intercept_errors = NGX_CONF_UNSET;
#if (NGX_HTTP_SSL)
    conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;
#endif

    /* "proxy_cyclic_temp_file" is disabled */
    conf->upstream.cyclic_temp_file = 0;

    conf->redirect = NGX_CONF_UNSET;
    conf->parse = NGX_CONF_UNSET;
    conf->change_content_length = NGX_CONF_UNSET;
    conf->tailor = 0;
    conf->container = NGX_CONF_UNSET;
    conf->upstream.change_buffering = 1;

    conf->headers_hash_max_size = NGX_CONF_UNSET_UINT;
    conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT;
    conf->max_shm_uri_item_limit = NGX_CONF_UNSET_UINT;

    ngx_str_set(&conf->upstream.module, "cacheproxy");

    return conf;
}


static char *
ngx_http_cacheproxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_cacheproxy_loc_conf_t *prev = parent;
    ngx_http_cacheproxy_loc_conf_t *conf = child;

    size_t                      size;
    ngx_hash_init_t             hash;
    ngx_http_core_loc_conf_t   *clcf;
    ngx_http_script_compile_t   sc;

    if (conf->upstream.store != 0) {
        ngx_conf_merge_value(conf->upstream.store,
                              prev->upstream.store, 0);

        if (conf->upstream.store_lengths == NULL) {
            conf->upstream.store_lengths = prev->upstream.store_lengths;
            conf->upstream.store_values = prev->upstream.store_values;
        }
    }

    ngx_conf_merge_uint_value(conf->upstream.store_access,
                              prev->upstream.store_access, 0600);

    ngx_conf_merge_value(conf->upstream.buffering,
                              prev->upstream.buffering, 1);

    ngx_conf_merge_value(conf->upstream.ignore_client_abort,
                              prev->upstream.ignore_client_abort, 0);

    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,
                              prev->upstream.connect_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.send_timeout,
                              prev->upstream.send_timeout, 60000);

    ngx_conf_merge_msec_value(conf->upstream.read_timeout,
                              prev->upstream.read_timeout, 60000);

    ngx_conf_merge_size_value(conf->upstream.send_lowat,
                              prev->upstream.send_lowat, 0);

    ngx_conf_merge_size_value(conf->upstream.buffer_size,
                              prev->upstream.buffer_size,
                              (size_t) ngx_pagesize);

    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,
                              8, ngx_pagesize);

    if (conf->upstream.bufs.num < 2) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "there must be at least 2 \"proxy_buffers\"");
        return NGX_CONF_ERROR;
    }


    size = conf->upstream.buffer_size;
    if (size < conf->upstream.bufs.size) {
        size = conf->upstream.bufs.size;
    }


    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,
                              prev->upstream.busy_buffers_size_conf,
                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {
        conf->upstream.busy_buffers_size = 2 * size;
    } else {
        conf->upstream.busy_buffers_size =
                                         conf->upstream.busy_buffers_size_conf;
    }

    if (conf->upstream.busy_buffers_size < size) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
             "\"proxy_busy_buffers_size\" must be equal or bigger than "
             "maximum of the value of \"proxy_buffer_size\" and "
             "one of the \"proxy_buffers\"");

        return NGX_CONF_ERROR;
    }

    if (conf->upstream.busy_buffers_size
        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)
    {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
             "\"proxy_busy_buffers_size\" must be less than "
             "the size of all \"proxy_buffers\" minus one buffer");

        return NGX_CONF_ERROR;
    }


    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,
                              prev->upstream.temp_file_write_size_conf,
                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {
        conf->upstream.temp_file_write_size = 2 * size;
    } else {
        conf->upstream.temp_file_write_size =
                                      conf->upstream.temp_file_write_size_conf;
    }

    if (conf->upstream.temp_file_write_size < size) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
             "\"proxy_temp_file_write_size\" must be equal or bigger than "
             "maximum of the value of \"proxy_buffer_size\" and "
             "one of the \"proxy_buffers\"");

        return NGX_CONF_ERROR;
    }

    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,
                              prev->upstream.max_temp_file_size_conf,
                              NGX_CONF_UNSET_SIZE);

    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {
        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;
    } else {
        conf->upstream.max_temp_file_size =
                                        conf->upstream.max_temp_file_size_conf;
    }

    if (conf->upstream.max_temp_file_size != 0
        && conf->upstream.max_temp_file_size < size)
    {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
             "\"proxy_max_temp_file_size\" must be equal to zero to disable "
             "the temporary files usage or must be equal or bigger than "
             "maximum of the value of \"proxy_buffer_size\" and "
             "one of the \"proxy_buffers\"");

        return NGX_CONF_ERROR;
    }


    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,
                              prev->upstream.ignore_headers,
                              NGX_CONF_BITMASK_SET);


    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,
                              prev->upstream.next_upstream,
                              (NGX_CONF_BITMASK_SET
                               |NGX_HTTP_UPSTREAM_FT_ERROR
                               |NGX_HTTP_UPSTREAM_FT_TIMEOUT));

    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {
        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET
                                       |NGX_HTTP_UPSTREAM_FT_OFF;
    }

    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,
                              prev->upstream.temp_path,
                              &ngx_http_proxy_temp_path)
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }


#if (NGX_HTTP_CACHE)

    ngx_conf_merge_ptr_value(conf->upstream.cache,
                              prev->upstream.cache, NULL);

    if (conf->upstream.cache && conf->upstream.cache->data == NULL) {
        ngx_shm_zone_t  *shm_zone;

        shm_zone = conf->upstream.cache;

        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "\"proxy_cache\" zone \"%V\" is unknown",
                           &shm_zone->shm.name);

        return NGX_CONF_ERROR;
    }

    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,
                              prev->upstream.cache_min_uses, 1);

    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,
                              prev->upstream.cache_use_stale,
                              (NGX_CONF_BITMASK_SET
                               |NGX_HTTP_UPSTREAM_FT_OFF));

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {
        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET
                                         |NGX_HTTP_UPSTREAM_FT_OFF;
    }

    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {
        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;
    }

    if (conf->upstream.cache_methods == 0) {
        conf->upstream.cache_methods = prev->upstream.cache_methods;
    }

    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;

    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,
                             prev->upstream.cache_bypass, NULL);

    ngx_conf_merge_ptr_value(conf->upstream.no_cache,
                             prev->upstream.no_cache, NULL);

    if (conf->upstream.no_cache && conf->upstream.cache_bypass == NULL) {
        ngx_log_error(NGX_LOG_WARN, cf->log, 0,
             "\"proxy_no_cache\" functionality has been changed in 0.8.46, "
             "now it should be used together with \"proxy_cache_bypass\"");
    }

    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,
                             prev->upstream.cache_valid, NULL);

    if (conf->cache_key.value.data == NULL) {
        conf->cache_key = prev->cache_key;
    }

#endif

    if (conf->method.len == 0) {
        conf->method = prev->method;

    } else {
        conf->method.data[conf->method.len] = ' ';
        conf->method.len++;
    }

    ngx_conf_merge_value(conf->upstream.pass_request_headers,
                              prev->upstream.pass_request_headers, 1);
    ngx_conf_merge_value(conf->upstream.pass_request_body,
                              prev->upstream.pass_request_body, 1);

    ngx_conf_merge_value(conf->upstream.intercept_errors,
                              prev->upstream.intercept_errors, 0);

#if (NGX_HTTP_SSL)
    ngx_conf_merge_value(conf->upstream.ssl_session_reuse,
                              prev->upstream.ssl_session_reuse, 1);
#endif

    ngx_conf_merge_value(conf->redirect, prev->redirect, 1);
    ngx_conf_merge_value(conf->parse, prev->parse, 0);
    ngx_conf_merge_value(conf->change_content_length, prev->change_content_length, 0);

    if(conf->tailor == 0){
      conf->tailor = prev->tailor;
    }  
    ngx_conf_merge_value(conf->container, prev->container, 0);

    if (conf->subs == NULL) {
        conf->subs = prev->subs;
    }


#if (NGX_HTTP_SSL)
    if (conf->upstream.ssl == NULL) {
        conf->upstream.ssl = prev->upstream.ssl;
    }
#endif

    ngx_conf_merge_uint_value(conf->headers_hash_max_size,
                              prev->headers_hash_max_size, 512);

    ngx_conf_merge_uint_value(conf->headers_hash_bucket_size,
                              prev->headers_hash_bucket_size, 64);

    ngx_conf_merge_uint_value(conf->max_shm_uri_item_limit,
                              prev->max_shm_uri_item_limit, MAX_SHM_URI_ITEM_LIMIT);

    conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size,
                                               ngx_cacheline_size);

    hash.max_size = conf->headers_hash_max_size;
    hash.bucket_size = conf->headers_hash_bucket_size;
    hash.name = "proxy_headers_hash";

    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,
            &prev->upstream, ngx_http_proxy_hide_headers, &hash)
        != NGX_OK)
    {
        return NGX_CONF_ERROR;
    }

    if (conf->upstream.upstream == NULL) {
        conf->upstream.upstream = prev->upstream.upstream;
        conf->vars = prev->vars;
    }

    if (conf->proxy_lengths == NULL) {
        conf->proxy_lengths = prev->proxy_lengths;
        conf->proxy_values = prev->proxy_values;
    }

    if (conf->upstream.upstream || conf->proxy_lengths) {
        clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
        if (clcf->handler == NULL && clcf->lmt_excpt) {
            clcf->handler = ngx_http_cacheproxy_handler;
            conf->location = prev->location;
        }
    }

    if (conf->body_source.data == NULL) {
        conf->body_source = prev->body_source;
        conf->body_set_len = prev->body_set_len;
        conf->body_set = prev->body_set;
    }

    if (conf->body_source.data && conf->body_set_len == NULL) {

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;
        sc.source = &conf->body_source;
        sc.flushes = &conf->flushes;
        sc.lengths = &conf->body_set_len;
        sc.values = &conf->body_set;
        sc.complete_lengths = 1;
        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
    }

    if (ngx_http_cacheproxy_merge_headers(cf, conf, prev) != NGX_OK) {
        return NGX_CONF_ERROR;
    }

    return NGX_CONF_OK;
}


static ngx_int_t
ngx_http_cacheproxy_merge_headers(ngx_conf_t *cf, ngx_http_cacheproxy_loc_conf_t *conf,
    ngx_http_cacheproxy_loc_conf_t *prev)
{
    u_char                       *p;
    size_t                        size;
    uintptr_t                    *code;
    ngx_uint_t                    i;
    ngx_array_t                   headers_names, headers_merged;
    ngx_keyval_t                 *src, *s, *h;
    ngx_hash_key_t               *hk;
    ngx_hash_init_t               hash;
    ngx_http_script_compile_t     sc;
    ngx_http_script_copy_code_t  *copy;

    if (conf->headers_source == NULL) {
        conf->flushes = prev->flushes;
        conf->headers_set_len = prev->headers_set_len;
        conf->headers_set = prev->headers_set;
        conf->headers_set_hash = prev->headers_set_hash;
        conf->headers_source = prev->headers_source;
    }

    if (conf->headers_set_hash.buckets
        && ((conf->body_source.data == NULL)
            == (prev->body_source.data == NULL))
#if (NGX_HTTP_CACHE)
        && ((conf->upstream.cache == NULL) == (prev->upstream.cache == NULL))
#endif
       )
    {
        return NGX_OK;
    }


    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    if (conf->headers_source == NULL) {
        conf->headers_source = ngx_array_create(cf->pool, 4,
                                                sizeof(ngx_keyval_t));
        if (conf->headers_source == NULL) {
            return NGX_ERROR;
        }
    }

    conf->headers_set_len = ngx_array_create(cf->pool, 64, 1);
    if (conf->headers_set_len == NULL) {
        return NGX_ERROR;
    }

    conf->headers_set = ngx_array_create(cf->pool, 512, 1);
    if (conf->headers_set == NULL) {
        return NGX_ERROR;
    }


#if (NGX_HTTP_CACHE)

    h = conf->upstream.cache ? ngx_http_proxy_cache_headers:
                               ngx_http_proxy_headers;
#else

    h = ngx_http_proxy_headers;

#endif

    src = conf->headers_source->elts;
    for (i = 0; i < conf->headers_source->nelts; i++) {

        s = ngx_array_push(&headers_merged);
        if (s == NULL) {
            return NGX_ERROR;
        }

        *s = src[i];
    }

    while (h->key.len) {

        src = headers_merged.elts;
        for (i = 0; i < headers_merged.nelts; i++) {
            if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {
                goto next;
            }
        }

        s = ngx_array_push(&headers_merged);
        if (s == NULL) {
            return NGX_ERROR;
        }

        *s = *h;

    next:

        h++;
    }

    if (conf->body_source.data) {
        s = ngx_array_push(&headers_merged);
        if (s == NULL) {
            return NGX_ERROR;
        }

        ngx_str_set(&s->key, "Content-Length");
        ngx_str_set(&s->value, "$cacheproxy_internal_body_length");
    }


    src = headers_merged.elts;
    for (i = 0; i < headers_merged.nelts; i++) {

        hk = ngx_array_push(&headers_names);
        if (hk == NULL) {
            return NGX_ERROR;
        }

        hk->key = src[i].key;
        hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len);
        hk->value = (void *) 1;

        if (src[i].value.len == 0) {
            continue;
        }

        if (ngx_http_script_variables_count(&src[i].value) == 0) {
            copy = ngx_array_push_n(conf->headers_set_len,
                                    sizeof(ngx_http_script_copy_code_t));
            if (copy == NULL) {
                return NGX_ERROR;
            }

            copy->code = (ngx_http_script_code_pt)
                                                 ngx_http_script_copy_len_code;
            copy->len = src[i].key.len + sizeof(": ") - 1
                        + src[i].value.len + sizeof(CRLF) - 1;


            size = (sizeof(ngx_http_script_copy_code_t)
                       + src[i].key.len + sizeof(": ") - 1
                       + src[i].value.len + sizeof(CRLF) - 1
                       + sizeof(uintptr_t) - 1)
                    & ~(sizeof(uintptr_t) - 1);

            copy = ngx_array_push_n(conf->headers_set, size);
            if (copy == NULL) {
                return NGX_ERROR;
            }

            copy->code = ngx_http_script_copy_code;
            copy->len = src[i].key.len + sizeof(": ") - 1
                        + src[i].value.len + sizeof(CRLF) - 1;

            p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);

            p = ngx_cpymem(p, src[i].key.data, src[i].key.len);
            *p++ = ':'; *p++ = ' ';
            p = ngx_cpymem(p, src[i].value.data, src[i].value.len);
            *p++ = CR; *p = LF;

        } else {
            copy = ngx_array_push_n(conf->headers_set_len,
                                    sizeof(ngx_http_script_copy_code_t));
            if (copy == NULL) {
                return NGX_ERROR;
            }

            copy->code = (ngx_http_script_code_pt)
                                                 ngx_http_script_copy_len_code;
            copy->len = src[i].key.len + sizeof(": ") - 1;


            size = (sizeof(ngx_http_script_copy_code_t)
                    + src[i].key.len + sizeof(": ") - 1 + sizeof(uintptr_t) - 1)
                    & ~(sizeof(uintptr_t) - 1);

            copy = ngx_array_push_n(conf->headers_set, size);
            if (copy == NULL) {
                return NGX_ERROR;
            }

            copy->code = ngx_http_script_copy_code;
            copy->len = src[i].key.len + sizeof(": ") - 1;

            p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
            p = ngx_cpymem(p, src[i].key.data, src[i].key.len);
            *p++ = ':'; *p = ' ';


            ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

            sc.cf = cf;
            sc.source = &src[i].value;
            sc.flushes = &conf->flushes;
            sc.lengths = &conf->headers_set_len;
            sc.values = &conf->headers_set;

            if (ngx_http_script_compile(&sc) != NGX_OK) {
                return NGX_ERROR;
            }


            copy = ngx_array_push_n(conf->headers_set_len,
                                    sizeof(ngx_http_script_copy_code_t));
            if (copy == NULL) {
                return NGX_ERROR;
            }

            copy->code = (ngx_http_script_code_pt)
                                                 ngx_http_script_copy_len_code;
            copy->len = sizeof(CRLF) - 1;


            size = (sizeof(ngx_http_script_copy_code_t)
                    + sizeof(CRLF) - 1 + sizeof(uintptr_t) - 1)
                    & ~(sizeof(uintptr_t) - 1);

            copy = ngx_array_push_n(conf->headers_set, size);
            if (copy == NULL) {
                return NGX_ERROR;
            }

            copy->code = ngx_http_script_copy_code;
            copy->len = sizeof(CRLF) - 1;

            p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);
            *p++ = CR; *p = LF;
        }

        code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t));
        if (code == NULL) {
            return NGX_ERROR;
        }

        *code = (uintptr_t) NULL;

        code = ngx_array_push_n(conf->headers_set, sizeof(uintptr_t));
        if (code == NULL) {
            return NGX_ERROR;
        }

        *code = (uintptr_t) NULL;
    }

    code = ngx_array_push_n(conf->headers_set_len, sizeof(uintptr_t));
    if (code == NULL) {
        return NGX_ERROR;
    }

    *code = (uintptr_t) NULL;


    hash.hash = &conf->headers_set_hash;
    hash.key = ngx_hash_key_lc;
    hash.max_size = conf->headers_hash_max_size;
    hash.bucket_size = conf->headers_hash_bucket_size;
    hash.name = "proxy_headers_hash";
    hash.pool = cf->pool;
    hash.temp_pool = NULL;

    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);
}

static char *
ngx_http_cacheproxy_token(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_str_t                       *value;
    ngx_http_core_loc_conf_t        *clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    //ngx_http_cacheproxy_main_conf_t *cpmf = ngx_http_conf_get_module_main_conf(cf,ngx_http_cacheproxy_module);

    value = cf->args->elts;

    if (ngx_strcmp(value[1].data, "off") == 0) {
        return NGX_CONF_OK;
    }

    /*ngx_int_t rc = ngx_atoi(value[1].data, value[1].len);
    if(rc == NGX_ERROR){
       return "invalid value";
    } 
    cpmf->tokenbegin  = rc;
    */
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "cpmf tokenbegin");
    clcf->handler = ngx_http_cacheproxy_token_handler;
    return NGX_CONF_OK;
}

char *
ngx_conf_set_mynum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{     
    char  *p = conf;
      
    ngx_int_t        *np;
    ngx_str_t        *value;
    ngx_conf_post_t  *post;


    np = (ngx_int_t *) (p + cmd->offset);
    
    if (*np != 0) {
        return "is duplicate";
    }
    
    value = cf->args->elts;
    *np = atoi((char*)value[1].data);

    
    if (cmd->post) {
        post = cmd->post;
        return post->post_handler(cf, post, np);
    }

    return NGX_CONF_OK;
}



static char *
ngx_http_cacheproxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_cacheproxy_loc_conf_t *plcf = conf;

    size_t                      add;
    u_short                     port;
    ngx_str_t                  *value, *url , *uri_item_limit;
    ngx_url_t                   u;
    ngx_uint_t                  n;
    ngx_http_core_loc_conf_t   *clcf;
    ngx_http_script_compile_t   sc;

    if (plcf->upstream.upstream || plcf->proxy_lengths) {
        return "is duplicate";
    }

    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);

    clcf->handler = ngx_http_cacheproxy_handler;

    if (clcf->name.data[clcf->name.len - 1] == '/') {
        clcf->auto_redirect = 1;
    }

    value = cf->args->elts;

    url = &value[1];
    uri_item_limit = cf->args->nelts==3?&value[2]:NULL;
    if(uri_item_limit)
        plcf->max_shm_uri_item_limit = ngx_atoi(uri_item_limit->data,uri_item_limit->len);

    n = ngx_http_script_variables_count(url);

    if (n) {

        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));

        sc.cf = cf;
        sc.source = url;
        sc.lengths = &plcf->proxy_lengths;
        sc.values = &plcf->proxy_values;
        sc.variables = n;
        sc.complete_lengths = 1;
        sc.complete_values = 1;

        if (ngx_http_script_compile(&sc) != NGX_OK) {
            return NGX_CONF_ERROR;
        }

#if (NGX_HTTP_SSL)
        if (ngx_http_proxy_set_ssl(cf, plcf) != NGX_OK) {
            return NGX_CONF_ERROR;
        }
#endif

        return NGX_CONF_OK;
    }

    if (ngx_strncasecmp(url->data, (u_char *) "http://", 7) == 0) {
        add = 7;
        port = 80;

    } else if (ngx_strncasecmp(url->data, (u_char *) "https://", 8) == 0) {

#if (NGX_HTTP_SSL)
        if (ngx_http_proxy_set_ssl(cf, plcf) != NGX_OK) {
            return NGX_CONF_ERROR;
        }

        add = 8;
        port = 443;
#else
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "https protocol requires SSL support");
        return NGX_CONF_ERROR;
#endif

    } else {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid URL prefix");
        return NGX_CONF_ERROR;
    }

    ngx_memzero(&u, sizeof(ngx_url_t));

    u.url.len = url->len - add;
    u.url.data = url->data + add;
    u.default_port = port;
    u.uri_part = 1;
    u.no_resolve = 1;

    plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);
    if (plcf->upstream.upstream == NULL) {
        return NGX_CONF_ERROR;
    }

    plcf->vars.schema.len = add;
    plcf->vars.schema.data = url->data;
    plcf->vars.key_start = plcf->vars.schema;

    ngx_http_cacheproxy_set_vars(&u, &plcf->vars);

    plcf->location = clcf->name;

    if (clcf->named
#if (NGX_PCRE)
        || clcf->regex
#endif
        || clcf->noname)
    {
        if (plcf->vars.uri.len) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "\"proxy_pass\" may not have URI part in "
                               "location given by regular expression, "
                               "or inside named location, "
                               "or inside the \"if\" statement, "
                               "or inside the \"limit_except\" block");
            return NGX_CONF_ERROR;
        }

        plcf->location.len = 0;
    }

    plcf->url = *url;

    return NGX_CONF_OK;
}

static ngx_int_t
ngx_http_cacheproxy_host_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data)
{
    ngx_http_cacheproxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_cacheproxy_module);

    if (ctx == NULL) {
        v->not_found = 1;
        return NGX_OK;
    }

    v->len = ctx->vars.host_header.len;
    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;
    v->data = ctx->vars.host_header.data;

    return NGX_OK;
}


static ngx_int_t
ngx_http_cacheproxy_port_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data)
{
    ngx_http_cacheproxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_cacheproxy_module);

    if (ctx == NULL) {
        v->not_found = 1;
        return NGX_OK;
    }

    v->len = ctx->vars.port.len;
    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;
    v->data = ctx->vars.port.data;

    return NGX_OK;
}


static ngx_int_t
ngx_http_cacheproxy_add_x_forwarded_for_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data)
{
    u_char  *p;

    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;

    if (r->headers_in.x_forwarded_for == NULL) {
        v->len = r->connection->addr_text.len;
        v->data = r->connection->addr_text.data;
        return NGX_OK;
    }

    v->len = r->headers_in.x_forwarded_for->value.len
             + sizeof(", ") - 1 + r->connection->addr_text.len;

    p = ngx_pnalloc(r->pool, v->len);
    if (p == NULL) {
        return NGX_ERROR;
    }

    v->data = p;

    p = ngx_copy(p, r->headers_in.x_forwarded_for->value.data,
                 r->headers_in.x_forwarded_for->value.len);

    *p++ = ','; *p++ = ' ';

    ngx_memcpy(p, r->connection->addr_text.data, r->connection->addr_text.len);

    return NGX_OK;
}


static ngx_int_t
ngx_http_cacheproxy_internal_body_length_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data)
{
    ngx_http_cacheproxy_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_cacheproxy_module);

    if (ctx == NULL) {
        v->not_found = 1;
        return NGX_OK;
    }

    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;

    v->data = ngx_pnalloc(r->connection->pool, NGX_SIZE_T_LEN);

    if (v->data == NULL) {
        return NGX_ERROR;
    }

    v->len = ngx_sprintf(v->data, "%uz", ctx->internal_body_length) - v->data;

    return NGX_OK;
}


static ngx_int_t
ngx_http_cacheproxy_add_variables(ngx_conf_t *cf)
{
    ngx_http_variable_t  *var, *v;

    for (v = ngx_http_cacheproxy_vars; v->name.len; v++) {
        var = ngx_http_add_variable(cf, &v->name, v->flags);
        if (var == NULL) {
            return NGX_ERROR;
        }

        var->get_handler = v->get_handler;
        var->data = v->data;
    }

    return NGX_OK;
}




static void
ngx_http_cacheproxy_set_vars(ngx_url_t *u, ngx_http_cacheproxy_vars_t *v)
{
    if (u->family != AF_UNIX) {

        if (u->no_port || u->port == u->default_port) {

            v->host_header = u->host;

            if (u->default_port == 80) {
                ngx_str_set(&v->port, "80");

            } else {
                ngx_str_set(&v->port, "443");
            }

        } else {
            v->host_header.len = u->host.len + 1 + u->port_text.len;
            v->host_header.data = u->host.data;
            v->port = u->port_text;
        }

        v->key_start.len += v->host_header.len;

    } else {
        ngx_str_set(&v->host_header, "localhost");
        ngx_str_null(&v->port);
        v->key_start.len += sizeof("unix:") - 1 + u->host.len + 1;
    }

    v->uri = u->uri;
}

static ngx_int_t
ngx_http_cacheproxy_postconfig(ngx_conf_t *cf)
{
    size_t                                  shm_size;
    size_t                                  shm_size_limit = 32 * ngx_pagesize;
    ngx_http_cacheproxy_main_conf_t *cpmf = ngx_http_conf_get_module_main_conf(cf,ngx_http_cacheproxy_module);

    ngx_conf_log_error(NGX_LOG_EMERG,cf,0,"the postconfig cacheproxy main_conf:%p cpmf->shmsize:%d shm_size_limit:%d",
                                          cpmf,cpmf->shmsize,shm_size_limit);
    if(cpmf->shmsize==0){
        return NGX_OK;
    }

    // initialize shared memory
    shm_size = ngx_align(cpmf->shmsize, ngx_pagesize);
    if (shm_size < shm_size_limit) {
        ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "The cacheproxy_shared_memory_size value must be at least %udKiB", shm_size_limit >> 10);
        shm_size = shm_size_limit;
    }
    if (ngx_http_cacheproxy_shm_zone && ngx_http_cacheproxy_shm_zone->shm.size != shm_size) {
        ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "Cannot change memory area size without restart, ignoring change");
    }
    ngx_conf_log_error(NGX_LOG_INFO, cf, 0, "Using %udKiB of shared memory for cacheproxy module", shm_size >> 10);

    // shared memory
    ngx_http_cacheproxy_shm_zone = ngx_shared_memory_add(cf, &ngx_http_cacheproxy_shm_name, shm_size, &ngx_http_cacheproxy_module);

    if (ngx_http_cacheproxy_shm_zone == NULL) {
        return NGX_ERROR;
    }

    ngx_http_cacheproxy_shm_zone->init = ngx_http_cacheproxy_init_shm_zone;
    ngx_http_cacheproxy_shm_zone->data = (void *)1;

    return NGX_OK;
}

// shared memory zone initializer
static ngx_int_t
ngx_http_cacheproxy_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data)
{

    if (data) { /* zone already initialized */
        shm_zone->data = data;
        return NGX_OK;
    }

    ngx_slab_pool_t                     *shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
    ngx_http_cacheproxy_shm_data_t       *d;

    if ((d = (ngx_http_cacheproxy_shm_data_t *) ngx_slab_alloc(shpool, sizeof(*d))) == NULL) { //shm_data plus an array.
        return NGX_ERROR;
    }
    shm_zone->data = d;
    ngx_queue_init(&d->uri_items);
    d->len = 0;
    //d->token = -1; 
    return NGX_OK;
}
